#ifndef XG_SHAREMEM_CPP
#define XG_SHAREMEM_CPP
////////////////////////////////////////////////////////
#include "../Sharemem.h"

Sharemem::Data::~Data()
{
	close();
}
u_char* Sharemem::Data::get() const
{
	return data;
}
Sharemem::Data::Data() : data(NULL)
{
}

Sharemem::Sharemem()
{
	created = false;
	handle = INVALID_HANDLE_VALUE;
}
Sharemem::~Sharemem()
{
	close();
}
u_char* Sharemem::get() const
{
	return canUse() ? data->get() : NULL;
}
bool Sharemem::canUse() const
{
	return data ? true : false;
}
u_char* Sharemem::open(HANDLE handle)
{
	if (HandleCanUse(handle))
	{
		close();

		this->handle = handle;

		data = newsp<Data>();

		if (data->init(handle)) return data->get();

		close();
	}

	return NULL;
}

#ifdef XG_LINUX

#include <sys/ipc.h>
#include <sys/shm.h>

void Sharemem::Data::close()
{
	if (data)
	{
		shmdt(data);
		data = NULL;
	}
}
bool Sharemem::Data::init(HANDLE handle)
{
	close();

	data = (u_char*)shmat(handle, 0, 0);

	return data ? true : false;
}

void Sharemem::close()
{
	if (canUse() && created) shmctl(handle, IPC_RMID, NULL);

	handle = INVALID_HANDLE_VALUE;
	created = false;
	data = NULL;
}
int Sharemem::size() const
{
	if (canUse())
	{
		struct shmid_ds info;

		if (shmctl(handle, IPC_STAT, &info) == 0) return info.shm_segsz;
	}

	return XG_ERROR;
}
u_char* Sharemem::open(const string& name)
{
	if (name.empty()) return NULL;

	key_t key = ftok(name.c_str(), 0);

	if (key < 0)
	{
		if (errno != 0 && errno != ENOENT && errno != ENOTDIR) return NULL;

		key = (key_t)(MD5GetUINT32(name.c_str(), name.length()) % 0xFFFF);
	}

	return open(shmget(key, 0, IPC_EXCL | XG_IPC_FLAG));
}
u_char* Sharemem::create(const string& name, int memsz)
{
	if (name.empty()) return NULL;

	key_t key = ftok(name.c_str(), 0);

	if (key < 0)
	{
		if (errno != 0 && errno != ENOENT && errno != ENOTDIR) return NULL;

		key = (key_t)(MD5GetUINT32(name.c_str(), name.length()) % 0xFFFF);
	}

	if (open(shmget(key, memsz, IPC_CREAT | IPC_EXCL | XG_IPC_FLAG))) created = true;

	return get();
}

#else

void Sharemem::Data::close()
{
	if (data)
	{
		UnmapViewOfFile(data);
		data = NULL;
	}
}
bool Sharemem::Data::init(HANDLE handle)
{
	close();

	data = (u_char*)MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, 0);

	return data ? true : false;
}

void Sharemem::close()
{
	if (canUse()) CloseHandle(handle);

	handle = INVALID_HANDLE_VALUE;
	created = true;
	data = NULL;
}
int Sharemem::size() const
{
	if (!data) return XG_ERROR;

	MEMORY_BASIC_INFORMATION info;

	if (VirtualQuery(data->get(), &info, sizeof(info)) == 0) return XG_ERROR;

	return info.RegionSize;
}
u_char* Sharemem::open(const string& name)
{
	if (name.empty()) return NULL;

	HANDLE handle = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, name.c_str());

	if (HandleCanUse(handle)) return open(handle);

	if (GetLastError() == ERROR_PATH_NOT_FOUND)
	{
		handle = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, MD5GetEncodeString(name.c_str(), name.length(), TRUE).val);
		
		if (HandleCanUse(handle)) return open(handle);
	}

	return NULL;
}
u_char* Sharemem::create(const string& name, int memsz)
{
	if (name.empty()) return NULL;

	HANDLE handle = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, memsz, name.c_str());

	if (HandleCanUse(handle)) return open(handle);

	if (GetLastError() == ERROR_PATH_NOT_FOUND)
	{
		handle = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, memsz, MD5GetEncodeString(name.c_str(), name.length(), TRUE).val);
		
		if (HandleCanUse(handle)) return open(handle);
	}

	return NULL;
}
#endif
////////////////////////////////////////////////////////
#endif